﻿/**
 * Peter
 * Created by: Peter Development Team
 *    http://peter.codeplex.com/
 * 
 * GNU General Public License version 2 (GPLv2)
 *    http://peter.codeplex.com/license
 *
 *  This code is provided on an AS IS basis, with no WARRANTIES,
 *  CONDITIONS or GUARANTEES of any kind.
 *
 **/
/*
Copyright (c) 2010, Daniel De Sousa (daniel@dandesousa.com)
http://code.google.com/p/phoenix-control-library/

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE
*/

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;

namespace Peter.Common.Controls
{
   /// <summary>
   /// Control for a numeric spinner.
   /// </summary>
   [TemplatePart(Name = ELEMENT_NUMERIC_TEXT_BOX, Type = typeof(TextBox))]
   [TemplatePart(Name = ELEMENT_INCREMENT_BUTTON, Type = typeof(RepeatButton))]
   [TemplatePart(Name = ELEMENT_DECREMENT_BUTTON, Type = typeof(RepeatButton))]
   public class NumericUpDown : RangeBase
   {
      #region Fields

      /// <summary>
      /// Template name of Text box.
      /// </summary>
      private const string ELEMENT_NUMERIC_TEXT_BOX = "PART_NumericTextBox";

      /// <summary>
      /// Template name of increment button.
      /// </summary>
      private const string ELEMENT_INCREMENT_BUTTON = "PART_IncrementButton";

      /// <summary>
      /// Template name of decrement button.
      /// </summary>
      private const string ELEMENT_DECREMENT_BUTTON = "PART_DecrementButton";

      /// <summary>
      /// Minimum interval.
      /// </summary>
      private const double MIN_INTERVAL = 0.01;

      /// <summary>
      /// Default interval.
      /// </summary>
      private const double DEFAULT_INTERVAL = 1.0;

      /// <summary>
      /// Text box with number.
      /// </summary>
      private TextBox m_NumericTextBox;

      /// <summary>
      /// Increment button.
      /// </summary>
      private RepeatButton m_IncrementButton;

      /// <summary>
      /// Decrement button.
      /// </summary>
      private RepeatButton m_DecrementButton;

      #endregion

      #region Setup

      /// <summary>
      /// Initilalizes a new numeric up down control.
      /// </summary>
      public NumericUpDown ()
      {
         this.Maximum = double.MaxValue;
         this.Minimum = 0.0;
      }

      /// <summary>
      /// Static constructor.
      /// </summary>
      static NumericUpDown ()
      {
         DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(typeof(NumericUpDown)));
      }

      #endregion

      #region Properties

      /// <summary>
      /// Precision for number being displayed.
      /// </summary>
      public int Precision
      {
         get { return (int)GetValue(PrecisionProperty); }
         set { SetValue(PrecisionProperty, value); }
      }

      /// <summary>
      /// DP for Precision.
      /// </summary>
      public static readonly DependencyProperty PrecisionProperty =
          DependencyProperty.Register("Precision", typeof(int), typeof(NumericUpDown), 
                                      new PropertyMetadata(2));

      /// <summary>
      /// Value to increment spinner by.
      /// </summary>
      public double IncrementValue
      {
         get { return (double)GetValue(IncrementValueProperty); }
         set { SetValue(IncrementValueProperty, value); }
      }

      /// <summary>
      /// DP for IncrementValue.
      /// </summary>
      public static readonly DependencyProperty IncrementValueProperty =
          DependencyProperty.Register("IncrementValue", typeof(double), typeof(NumericUpDown),
             new FrameworkPropertyMetadata(DEFAULT_INTERVAL, OnIntervalTimeChanged, CoerceInterval), IsValidInterval);

      #endregion

      #region Methods

      /// <summary>
      /// Checks to see if the interval is valid.
      /// </summary>
      /// <param name="value">Value to check.</param>
      /// <returns>True if valid, otherwise false.</returns>
      private static bool IsValidInterval (object value)
      {
         var val = (double)value;
         return val >= MIN_INTERVAL;
      }

      /// <summary>
      /// Corrects interval value to be valid.
      /// </summary>
      /// <param name="d">DependencyObject</param>
      /// <param name="value">Value to check.</param>
      /// <returns>Valid interval.</returns>
      private static object CoerceInterval (DependencyObject d, object value)
      {
         var val = (double)value;
         if (val < MIN_INTERVAL)
            return MIN_INTERVAL;
         return val;
      }

      /// <summary>
      /// Occurs when the interval changes.
      /// </summary>
      /// <param name="d">DependencyObject</param>
      /// <param name="args">DependencyPropertyChangedEventArgs</param>
      protected static void OnIntervalTimeChanged (DependencyObject d,
         DependencyPropertyChangedEventArgs args)
      {
      }

      /// <summary>
      /// When applying the template, load the parts into the data fields 
      /// of this Custom Control
      /// </summary>
      public override void OnApplyTemplate ()
      {
         base.OnApplyTemplate();

         m_NumericTextBox = GetTemplateChild(ELEMENT_NUMERIC_TEXT_BOX) as TextBox;
         if (m_NumericTextBox != null)
         {
            m_NumericTextBox.IsReadOnly = true;
            var tbBinding = new Binding ("Value")
                               { Source = this, Mode = BindingMode.TwoWay, StringFormat = "{0:F" + Precision + "}" };
            m_NumericTextBox.SetBinding(TextBox.TextProperty, tbBinding);
         }

         m_IncrementButton = GetTemplateChild(ELEMENT_INCREMENT_BUTTON) as RepeatButton;
         if (m_IncrementButton != null)
         {
            m_IncrementButton.Click += IncrementButtonClick;
         }

         m_DecrementButton = GetTemplateChild(ELEMENT_DECREMENT_BUTTON) as RepeatButton;
         if (m_DecrementButton != null)
         {
            m_DecrementButton.Click += DecrementButtonClick;
         }
      }

      /// <summary>
      /// Occurs when the increment button is pressed.
      /// </summary>
      /// <param name="sender">Increment button</param>
      /// <param name="e">RoutedEventArgs</param>
      void IncrementButtonClick (object sender, RoutedEventArgs e)
      {
         this.Value += this.IncrementValue;
      }

      /// <summary>
      /// Occurs when the decrement button is pressed.
      /// </summary>
      /// <param name="sender">Decrement button</param>
      /// <param name="e">RoutedEventArgs</param>
      void DecrementButtonClick (object sender, RoutedEventArgs e)
      {
         this.Value -= this.IncrementValue;
      }

      #endregion
   }
}
